import org.junit.jupiter.api.Test;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.*;


/**
 * @author xufeng
 * @date 2023-04-26
 */
public class LocalTimeDemo {

    /**
     * MAX：localTime对象能表示时间的最大值。
     * MIN：localTime对象能表示时间的最小值。
     * MIDNIGHT：午夜 00：00 点
     * NOON：中午 12：00 点
     */
    @Test
    public void test01(){
        System.out.println(LocalTime.MAX); // 输出 23:59:59.999999999
        System.out.println(LocalTime.MIN); // 输出 00:00
        System.out.println(LocalTime.MIDNIGHT); // 输出 00:00
        System.out.println(LocalTime.NOON); // 输出 12:00
    }

}

class LocalTimeDemo01 {

    /**
     * from(TemporalAccessor temporal)
     * 作用：从其他含有时分秒数据的时间类获取localTime对象。
     */
    @Test
    public void test01(){
        LocalDateTime dateTime01 = LocalDateTime.parse("2023-01-01T12:00:00");
        System.out.println(LocalTime.from(dateTime01)); // 输出 12:00
    }

    /**
     * now()
     * 作用：从操作系统获取时间数据。
     *
     * now(ZoneId zone)
     * 作用：获取其他时区此刻的时间。
     *
     * now(Clock clock)
     * 作用：从clock对象获取时间数据。clock对象有时区的概念，所以获取的是指定地区的时间。
     */
    @Test
    public void test02(){
        System.out.println(LocalTime.now()); // 输出 15:06:58.569

        ZoneId zoneId01 = ZoneId.of("America/Los_Angeles");
        System.out.println(LocalTime.now(zoneId01)); // 输出 00:06:58.570

        Instant instant01 = Instant.parse("2023-04-01T10:00:00Z");
        Clock fixedClock01 = Clock.fixed(instant01, zoneId01);
        System.out.println(LocalTime.now(fixedClock01)); // 输出 03:00
    }

    /**
     * of(int hour, int minute)
     * of(int hour, int minute, int second)
     * of(int hour, int minute, int second, int nanoOfSecond)
     * 作用：给定时分秒纳秒数据创建localTime对象。
     *
     * ofSecondOfDay(long secondOfDay)
     * ofNanoOfDay(long nanoOfDay)
     * 作用：给定距离00:00的秒数或纳秒数创建localTime对象。
     */
    @Test
    public void test03(){
        System.out.println(LocalTime.of(12, 30)); // 输出 12:30
        System.out.println(LocalTime.of(12, 30, 45)); // 输出 12:30:45
        System.out.println(LocalTime.of(12, 30, 45, 100)); // 输出 12:30:45.000000100

        System.out.println(LocalTime.ofSecondOfDay(0)); // 输出 00:00
        System.out.println(LocalTime.ofSecondOfDay(1 * 60 * 60 + 1)); // 输出 01:00:01
        System.out.println(LocalTime.ofNanoOfDay(3 * 60 * 60 * 1000_000_000L + 1)); // 输出 03:00:00.000000001
    }

    /**
     * parse(CharSequence text)
     * parse(CharSequence text, DateTimeFormatter formatter)
     * 作用：将字符串解析成localTme对象，默认格式HH:mm或HH:mm:ss，formatter参数用来指定字符串的格式。
     */
    @Test
    public void test04(){
        System.out.println(LocalTime.parse("12:30")); // 输出 12:30
        System.out.println(LocalTime.parse("12:30:45")); // 输出 12:30:45

        DateTimeFormatter df01 = DateTimeFormatter.ofPattern("HH/mm/ss");
        System.out.println(LocalTime.parse("12/30/45", df01)); // 输出 12:30:45
    }
}

class LocalTimeDemo02 {

    /**
     * atDate(LocalDate date)
     * 作用：补充年月日数据后生成localDateTime对象。
     */
    @Test
    public void test01(){
        LocalTime time01 = LocalTime.parse("12:30:45");
        LocalDate date01 = LocalDate.parse("2023-01-01");
        System.out.println(time01.atDate(date01)); // 输出 2023-01-01T12:30:45
    }

    /**
     * atOffset(ZoneOffset offset)
     * 作用：时间不变，仅和offset偏移量组合生成offsetTime对象。
     */
    @Test
    public void test02(){
        LocalTime time01 = LocalTime.parse("12:00");
        ZoneOffset zoneOffset01 = ZoneOffset.of("+08:00");
        System.out.println(time01.atOffset(zoneOffset01)); // 输出 12:00+08:00
    }

    /**
     * compareTo(LocalTime other)
     * isAfter(LocalTime other)
     * isBefore(LocalTime other)
     * 作用：比较两个localTime对象的大小，other为null时报NPE异常。
     */
    @Test
    public void test03(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        LocalTime time02 = LocalTime.of(12, 30, 45);
        LocalTime time03 = LocalTime.of(12, 30, 50);
        LocalTime time04 = null;

        System.out.println(time01.compareTo(time02)); // 输出 0
        System.out.println(time01.compareTo(time03)); // 输出 -1
        System.out.println(time01.compareTo(time04)); // NPE异常

        System.out.println(time01.isBefore(time03)); // 输出 true
        System.out.println(time01.isBefore(time04)); // NPE异常
    }

    /**
     * format(DateTimeFormatter formatter)
     * 作用：按指定格式化输出字符串。
     */
    @Test
    public void test04(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        DateTimeFormatter df01 = DateTimeFormatter.ofPattern("HH时mm分ss秒");
        System.out.println(time01.format(df01)); // 输出 12时30分45秒
    }

    /**
     * isSupported(TemporalField field)
     * 作用：判断对象是否支持指定的时间分量。
     *
     * isSupported(TemporalUnit unit)
     * 作用：判断对象是否支持指定的时间单位。时间单位有年、月、日、时、分、秒、毫秒、纳秒、微妙等等。
     */
    @Test
    public void test05(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        System.out.println(time01.isSupported(ChronoField.HOUR_OF_DAY)); // 输出 true
        System.out.println(time01.isSupported(ChronoField.YEAR)); // 输出 false

        System.out.println(time01.isSupported(ChronoUnit.HOURS)); // 输出 true
        System.out.println(time01.isSupported(ChronoUnit.YEARS)); // 输出 false
    }

    /**
     * get(TemporalField field)
     * getLong(TemporalField field)
     * 作用：获取指定的时间分量，如年、月、日、时、分、秒、星期、一年中的第几天等等，详细可查看ChronoField类。
     *      但不是所有时间分量都能获取。如localTime对象无法获取跟年、月、日相关的时间分量。
     *
     * getHour()
     * getMinute()
     * getNano()
     * getSecond()
     * 作用：作用类似get(TemporalField field)。
     */
    @Test
    public void test06(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        System.out.println(time01.get(ChronoField.HOUR_OF_DAY)); // 输出 12
        System.out.println(time01.get(ChronoField.MINUTE_OF_HOUR)); // 输出 30
        System.out.println(time01.get(ChronoField.MINUTE_OF_DAY)); // 输出 750

        System.out.println(time01.getHour()); // 输出 12
        System.out.println(time01.getMinute()); // 输出 30
        System.out.println(time01.getSecond()); // 输出 45
        System.out.println(time01.getNano()); // 输出 0
    }

    /**
     * minus(long amountToSubtract, TemporalUnit unit)
     * minus(TemporalAmount amountToSubtract)
     * minusHours(long hoursToSubtract)
     * minusMinutes(long minutesToSubtract)
     * minusNanos(long nanosToSubtract)
     * minusSeconds(long secondsToSubtract)
     * 作用：在指定的时间单位上减去固定值。
     */
    @Test
    public void test07(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        System.out.println(time01.minus(5, ChronoUnit.SECONDS)); // 输出 12:30:40

        Duration duration01 = Duration.ofSeconds(5);
        System.out.println(time01.minus(duration01)); // 输出 12:30:40

        System.out.println(time01.minusHours(2)); // 输出 10:30:45
        System.out.println(time01.minusMinutes(15)); // 输出 12:15:45
    }

    /**
     * plus(long amountToSubtract, TemporalUnit unit)
     * plus(TemporalAmount amountToSubtract)
     * plusHours(long hoursToAdd)
     * plusMinutes(long minutesToAdd)
     * plusNanos(long nanosToAdd)
     * plusSeconds(long secondstoAdd)
     * 作用：在指定的时间单位上加上固定值。
     */
    @Test
    public void test08(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        System.out.println(time01.plus(5, ChronoUnit.SECONDS)); // 输出 12:30:50

        Duration duration01 = Duration.ofSeconds(5);
        System.out.println(time01.plus(duration01)); // 输出 12:30:50

        System.out.println(time01.plusHours(2)); // 输出 14:30:45
        System.out.println(time01.plusMinutes(15)); // 输出 12:45:45
    }

    /**
     * toSecondOfDay()
     * toNanoOfDay()
     * 作用：计算localTime距离00:00的秒数或纳秒数。
     */
    @Test
    public void test09(){
        LocalTime time01 = LocalTime.of(1, 0, 0);
        System.out.println(time01.toSecondOfDay()); // 输出 3600
        System.out.println(time01.toNanoOfDay()); // 输出 3600000000000
    }

    /**
     * query(TemporalQuery<R> query)
     * 作用：TemporalQuery是一个函数接口，包含一个queryFrom(TemporalAccessor temporal)方法。
     *      其中的temporal对象就是调用query()方法的对象。通过实现TemporalQuery接口来自定义查询日期的逻辑，
     *      比如我有个需求，需要经常计算localTime距离12:00还差多少秒，官方没有提供这样的查询逻辑而我们又
     *      经常需要用到，此时我们可以实现TemporalQuery接口来自定义查询逻辑。
     *      TemporalQueries类已经为我们定义了常用的查询逻辑。
     */
    @Test
    public void test10(){
        TemporalQuery<Integer> toSecondOfNoon = time ->{
            if(time.isSupported(ChronoField.HOUR_OF_DAY) &&
                    time.isSupported(ChronoField.MINUTE_OF_HOUR) &&
                    time.isSupported(ChronoField.SECOND_OF_MINUTE)){

                int noon = LocalTime.NOON.toSecondOfDay();
                int secondsPerMinute = 60;
                int secodsPerHour = 3600;

                int hour = time.get(ChronoField.HOUR_OF_DAY);
                int minute = time.get(ChronoField.MINUTE_OF_HOUR);
                int second = time.get(ChronoField.SECOND_OF_MINUTE);

                int total = hour * secodsPerHour;
                total += minute * secondsPerMinute;
                total += second;

                return noon - total;
            }
            return null;
        };

        LocalTime time01 = LocalTime.of(11, 59, 59);
        LocalTime time02 = LocalTime.of(11, 0, 0);
        System.out.println(time01.query(toSecondOfNoon)); // 输出 1
        System.out.println(time02.query(toSecondOfNoon)); // 输出 3600
    }

    /**
     * range(TemporalField field)
     * 作用：输出与localTime对象相关的时间分量值的范围。
     */
    @Test
    public void test11(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        System.out.println(time01.range(ChronoField.HOUR_OF_DAY)); // 输出 0 - 23
        System.out.println(time01.range(ChronoField.SECOND_OF_MINUTE)); // 输出 0 - 59
    }

    /**
     * truncatedTo(TemporalUnit unit)
     * 作用：根据时间单位截取localTime对象。
     */
    @Test
    public void test12(){
        LocalTime time01 = LocalTime.of(12, 30, 45, 100);
        System.out.println(time01.truncatedTo(ChronoUnit.SECONDS)); // 输出 12:30:45
        System.out.println(time01.truncatedTo(ChronoUnit.MINUTES)); // 输出 12:30
        System.out.println(time01.truncatedTo(ChronoUnit.HOURS)); // 输出 12:00
    }

    /**
     * until(Temporal endExclusive, TemporalUnit unit)
     * 作用：根据时间单位计算两个localTime对象的差值。
     */
    @Test
    public void test13(){
        LocalTime time01 = LocalTime.of(11, 15, 25);
        LocalTime time02 = LocalTime.of(12, 30, 45);
        LocalDateTime dateTime01 = LocalDateTime.of(2023, 1,1, 12, 30, 45);

        System.out.println(time01.until(time02, ChronoUnit.HOURS)); // 输出 1
        System.out.println(time01.until(time02, ChronoUnit.MINUTES)); // 输出 75
        System.out.println(time01.until(dateTime01, ChronoUnit.SECONDS)); // 输出 4520
    }

    /**
     * with(TemporalField field, long newValue)
     * withHour(int hour)
     * withMinute(int minute)
     * withNano(int nanoOfSecond)
     * withSecond(int second)
     * 作用：调整跟localDate对象相关的时间分量的值。
     *
     * with(TemporalAdjuster adjuster)
     * 作用：该方法跟query(TemporalQuery<R> query)类似，query()方法是根据给定的查询逻辑查询localTime对象。
     * 而with()方法是根据给定的修改逻辑修改localTime对象，java8的所有时间类都是不可变的，所以修改后返回的都是新的localTime对象。
     * TemporalAdjuster也是一个函数接口，包含Temporal adjustInto(Temporal temporal)方法。
     * 其中temporal对象就是调用with()方法的对象，通过实现TemporalAdjuster接口实现对localTime对象的修改逻辑。
     */
    @Test
    public void test14(){
        LocalTime time01 = LocalTime.of(12, 30, 45);
        LocalTime time02 = LocalTime.of(12, 30, 15);
        System.out.println(time01.with(ChronoField.HOUR_OF_DAY, 18)); // 输出 18:30:45
        System.out.println(time01.with(ChronoField.MINUTE_OF_HOUR, 45)); // 输出 12:45:45

        System.out.println(time01.withHour(18)); // 输出 18:30:45
        System.out.println(time01.withMinute(45)); // 输出 12:45:45

        /** 对时间进行四舍五入，大于30秒分+1，小于30秒抹去 **/
        TemporalAdjuster adjuster = time -> {
            if (time.isSupported(ChronoField.SECOND_OF_MINUTE) && time.isSupported(ChronoField.MINUTE_OF_HOUR)) {
                int seconds = time.get(ChronoField.SECOND_OF_MINUTE);
                time = time.with(ChronoField.SECOND_OF_MINUTE, 0);
                Temporal result;
                if (seconds >= 30) {
                    int minute = time.get(ChronoField.MINUTE_OF_HOUR);
                    time = time.with(ChronoField.MINUTE_OF_HOUR, minute + 1);
                }
                return time;
            }

            return null;
        };
        System.out.println(time01.with(adjuster)); // 输出 12:31
        System.out.println(time02.with(adjuster)); // 输出 12:30
    }
}